//---------------------------------------------------------------------------------
// Copyright (c) 2008 VoiceLynx Project
//
// This software is provided 'as-is', without any express or implied
// warranty. In no event will the authors be held liable for any damages
// arising from the use of this software.
//
// Permission is granted to anyone to use this software for any purpose,
// including commercial applications, and to alter it and redistribute it
// freely, subject to the following restrictions:
//
//     1. The origin of this software must not be misrepresented; you must not
//        claim that you wrote the original software. If you use this software
//        in a product, an acknowledgment in the product documentation would be
//        appreciated but is not required.
//
//     2. Altered source versions must be plainly marked as such, and must not be
//        misrepresented as being the original software.
//
//     3. This notice may not be removed or altered from any source
//        distribution.
//---------------------------------------------------------------------------------

#ifndef __VLPacketTranslator_h__
#define __VLPacketTranslator_h__

#include "VLPackets.h"

typedef std::map<VLHostId, VLPeer *> VLHostIdPeerMap;
typedef std::map<VLPeer *, VLHostId> VLPeerHostIdMap;

class VLPacketLogic;

struct VLIPacketHandler
{
public:
  ~VLIPacketHandler() { }
  void handlePacket(VLPeer *user, InDataStream &ds) { _invoke(user, ds); }

private:
  virtual void _invoke(VLPeer *user, InDataStream &ds) = 0;
};

template <class OwnerT, class PacketT>
class VLPacketHandler : public VLIPacketHandler
{
public:
  typedef void (OwnerT::*MemberFunction)(VLPeer *, PacketT&);

  VLPacketHandler(OwnerT* instance, MemberFunction memFn) : mInstance(instance), mFunction(memFn) {};

private:
  void _invoke(VLPeer *user, InDataStream &ds)
  {
    PacketT p(ds);
    (mInstance->*mFunction)(user, p);
  }

  OwnerT* mInstance;
  MemberFunction mFunction;
};

struct VLDisconnectRequest
{
  VLHostId host;
  VLEDisconnectReason reason;
};

typedef std::auto_ptr<VLDisconnectRequest> VLDisconnectRequestPtr;
typedef std::deque<VLDisconnectRequest *> VLDisconnectRequestQueue;

class VLPacketTranslator
{
public:
  VLPacketTranslator(VLPacketLogic &packetLogic);
  virtual ~VLPacketTranslator();

  VLRawOutgoingPacketPtr takeOutgoingData();
  void giveIncomingData(VLRawIncomingPacketPtr rawPacket);

  VLDisconnectRequestPtr takeDisconnectRequest();

  void onConnect(VLHostId hostId);
  void onDisconnect(VLHostId hostId);

  bool shouldDisconnect(VLHostId &outHostId, VLEDisconnectReason &outReason);

private:
  friend VLPacketLogic;
  void sendPacket(VLPeer *user, VLBasePacket &packet);
  void sendPacket(std::vector<VLPeer *> users, VLBasePacket &packet);
  void broadcastPacket(VLBasePacket &packet, VLPeer *exceptTo = 0);
  void disconnectFromPeer(VLPeer *peer, VLEDisconnectReason reason);

  //! Register a function to be called on the receipt of a certain type of packet
  template <class OwnerT, class PacketT>
  void registerMessageHandler(VLEPacketType packetType, OwnerT* obj, void (OwnerT::*memFn)(VLPeer *, PacketT &))
  {
    if (mPacketHandlers.find(packetType) == mPacketHandlers.end())
    {
      VLIPacketHandler *handler = new VLPacketHandler<OwnerT, PacketT>(obj, memFn);
      mPacketHandlers[packetType] = handler;
    }
    else
    {
      VL_ASSERT_ALWAYS();
    }
  }

  //! Unregister a function previously registered.
  void unregisterMessageHandler(VLEPacketType packetType)
  {
    VLPacketHandlerMap::iterator iter = mPacketHandlers.find(packetType);

    if (iter != mPacketHandlers.end())
    {
      delete iter->second;
      mPacketHandlers.erase(iter);
    }
    else
    {
      VL_ASSERT_ALWAYS();
    }
  }

  VLPeer *getUserFromHostId(VLHostId hostId);
  VLHostId getHostIdFromUser(VLPeer *user);

  VLPacketLogic        &mLogic;
  VLRawOutgoingPackets  mOutgoing;

  VLHostIdPeerMap mHostIdToPeerMap;
  VLPeerHostIdMap mPeerToHostIdMap;

  VLDisconnectRequestQueue mDisconnectRequests;

  typedef std::map<VLEPacketType, VLIPacketHandler *> VLPacketHandlerMap;
  VLPacketHandlerMap mPacketHandlers;
};

#define REG_PACKET_HANDLER(_packetType, _fullFnName)                              \
if (init)                                                                         \
{                                                                                 \
  registerMessageHandler(_packetType, this, &_fullFnName);                        \
}                                                                                 \
else                                                                              \
{                                                                                 \
  unregisterMessageHandler(_packetType);                                          \
}

#endif